/* Gauge - Performance Measurement Framework for Mobile Platforms
 * Copyright (C) 2013, Juha Hytonen <juha.a.hytonen@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.gauge.GaugeServer;

import java.util.List;
import java.util.logging.Logger;

import org.gauge.AbstractConnectionManager.ConnectionManagerCb;
import org.gauge.Log;
import org.gauge.LogEntry;
import org.gauge.Measurement;
import org.gauge.MeasurementStorage;
import org.gauge.Protocol;

/**
 * GaugeServer main.
 */
public class GaugeServer implements ConnectionManagerCb {

    private static final Logger log = Logger.getLogger("org.gauge.GaugeServer");
	private static final GaugeServer server = new GaugeServer();

	private boolean requestQuit;
	private ConnectionManager connectionManager;
	
    public static void main(String[] args) {   	
        Arguments.Parse(args);
        printGreeting();
        server.WaitForQuit();
    }
    
    public static void printGreeting() {
    	System.out.println("");
    	System.out.println("Welcome to Gauge Server!");
    	System.out.println("Please connect your agent now...");
    }
    
    public static void RequestQuit() {
    	server.requestQuit = true;
    	server.notify();
    }
    
    public GaugeServer() {
    	requestQuit = false;

        connectionManager = new ConnectionManager();
        connectionManager.AddCallback(this);
        connectionManager.Start();
    }
    
    public void WaitForQuit() {
		while(!requestQuit) {
			try {
				synchronized(this) {
					this.wait();
				}
			}
			catch(InterruptedException e) {
			}
    	}
    }

	@Override
	public void ConnectedEvent() {
		log.info("Connected to agent");
	}

	@Override
	public void DisconnectedEvent() {
		log.info("Disconnected from agent");
		try {
			closeCurrentBenchmark();
		}
		catch(Exception e) {
			log.warning(e.toString());
		}
	}

	@Override
	public void DataEvent(String pData) {
		List<String> messages = Protocol.GetMessages(pData);
		handleMessages(messages);
	}
	
	private void handleMessages(List<String> messages) {
		for(String msg : messages) {
			handleMessage(msg);
		}
	}
	
	private void handleMessage(String msg) {
    	try {
			if(Protocol.IsStartBenchmarkMsg(msg)) {
				handleStartBenchmark(msg);
			}
			else if(Protocol.IsMeasurementMsg(msg)) {
				handleMeasurement(msg);
			}
			else if(Protocol.IsLogMsg(msg)) {
				handleLogEntry(msg);
			}
			else if(Protocol.IsEndBenchmarksMsg(msg)) {
				handleEndBenchmarks();
			}
			else  {
				log.severe("Unrecognized message");
				log.severe(msg);
			}
		} 
    	catch (Exception e) {
			log.severe("Error processing message");
			log.severe(msg);
			log.severe(e.toString());
			e.printStackTrace();
		}
	}
	
	private void handleStartBenchmark(String msg) throws Exception {
		String benchmarkName = Protocol.ParseBenchmarkNameFrom(msg);
		log.info("Running benchmark " + benchmarkName);
		
		closeCurrentBenchmark();
		
		StorageFactory factory = new StorageFactoryImpl();
		Log benchmarkLog = factory.CreateLog(benchmarkName);
		Log.Register(benchmarkLog);
		MeasurementStorage measurementStorage = factory.CreateMeasurementStorage(benchmarkName);
    	MeasurementStorage.Register(measurementStorage);
	}
	
	private void closeCurrentBenchmark() throws Exception {
		Log.Close();
		if(MeasurementStorage.IsRegistered()) {
			MeasurementStorage.Close();
		}
	}
	
	private void handleMeasurement(String msg) throws Exception {
		Measurement measurement = Protocol.ParseMeasurementFrom(msg);
		MeasurementStorage.Add(measurement);
	}
	
	private void handleLogEntry(String msg) throws Exception {
		LogEntry entry = Protocol.ParseLogEntryFrom(msg);
		Log.AddEntry(entry);
	}
	
	private void handleEndBenchmarks() throws Exception {
		log.info("Benchmarks finished");
		closeCurrentBenchmark();
	}

	@Override
	public void ConnectionFailedEvent() {
		log.severe("Connection to agent failed");
	}
	    
}
